
#include "game_audio.h"
#include "game_intrinsicts.h"
#include "game.h"
internal void GameOutputSound(game_state *GameState, game_sound_output_buffer *SoundBuffer,
                              int ToneHz) {
    int16 ToneVolume = 2000;
    int WavePeriod = SoundBuffer->SamplesPerSecond / ToneHz;
    int16 *SampleOut = SoundBuffer->Samples;
    // 循环写入样本到第一段区域
    for (int SampleIndex = 0; SampleIndex < SoundBuffer->SampleCount; ++SampleIndex) {
        real32 SineValue = sinf(GameState->tSine);
        int16 SampleValue = (int16)(SineValue * ToneVolume);
        *SampleOut++ = SampleValue;  // 左声道
        *SampleOut++ = SampleValue;  // 右声道
        GameState->tSine += Tau32 * 1.0f / (real32)WavePeriod;
        if (GameState->tSine > Tau32) {
            GameState->tSine -= Tau32;
        }
    }
}

internal void OutputPlayingSound(audio_state *AudioState, game_sound_output_buffer *SoundBuffer,
                                 game_assets *Assets, memory_arena TransientArena) {
    temporary_memory MixerMemory = BeginTemporaryMemory(&TransientArena);
    uint32 GenerationID = BeginGeneration(Assets);
    Assert((SoundBuffer->SampleCount & 3) == 0);
    uint32 ChunkCount = SoundBuffer->SampleCount / 4;

    __m128 *RealChannel0 = PushArray(&TransientArena, ChunkCount, __m128, AlignNoClear(16));
    __m128 *RealChannel1 = PushArray(&TransientArena, ChunkCount, __m128, AlignNoClear(16));

    real32 SecondsPerSample = 1.0f / (real32)SoundBuffer->SamplesPerSecond;
#define AudioStateOutputChannelCount 2
    // 清空混音通道
    __m128 One = _mm_set1_ps(1.0f);
    {
        __m128 *Dest0 = RealChannel0;
        __m128 *Dest1 = RealChannel1;
#if 0
        for (uint32 SampleIndex = 0;  //
             SampleIndex < ChunkCount; ++SampleIndex) {
            _mm_store_ps((real32 *)Dest0++, Zero);
            _mm_store_ps((real32 *)Dest1++, Zero);
        }
#else
        memset(Dest0, 0, ChunkCount * sizeof(__m128));
        memset(Dest1, 0, ChunkCount * sizeof(__m128));
#endif
    }
    // 汇总所有声音
    for (playing_sound **PlayingSoundPtr = &AudioState->FirstPlayingSound;  //
         *PlayingSoundPtr;                                                  //
    ) {
        playing_sound *PlayingSound = *PlayingSoundPtr;
        bool32 SoundFinished = false;
        uint32 TotalChunksToMix = ChunkCount;
        __m128 *Dest0 = RealChannel0;
        __m128 *Dest1 = RealChannel1;
        while (TotalChunksToMix && !SoundFinished) {
            loaded_sound *LoadedSound = GetSound(Assets, PlayingSound->ID, GenerationID);
            if (LoadedSound) {
                sound_id NextSoundInChina = GetNextSoundInChain(Assets, PlayingSound->ID);
                PrefetchSound(Assets, NextSoundInChina);
                v2 Volume = PlayingSound->CurrentVolume;
                v2 dVolume = SecondsPerSample * PlayingSound->dCurrentVolume;
                v2 dVolumeChunk = 4.0f * dVolume;
                real32 dSample = PlayingSound->dSample * 3.0f;
                real32 dSampleChunk = 4.0f * dSample;

                __m128 MasterVolume0 = _mm_set1_ps(AudioState->MasterVolume.E[0]);
                __m128 MasterVolume1 = _mm_set1_ps(AudioState->MasterVolume.E[1]);
                __m128 dVolumeChunk0 = _mm_set1_ps(dVolumeChunk.E[0]);
                __m128 dVolumeChunk1 = _mm_set1_ps(dVolumeChunk.E[1]);
                __m128 Volume0 = _mm_setr_ps(Volume.E[0] + 0.0f * dVolume.E[0],  //
                                             Volume.E[0] + 1.0f * dVolume.E[0],  //
                                             Volume.E[0] + 2.0f * dVolume.E[0],  //
                                             Volume.E[0] + 3.0f * dVolume.E[0]);
                __m128 Volume1 = _mm_setr_ps(Volume.E[1] + 0.0f * dVolume.E[1],  //
                                             Volume.E[1] + 1.0f * dVolume.E[1],  //
                                             Volume.E[1] + 2.0f * dVolume.E[1],  //
                                             Volume.E[1] + 3.0f * dVolume.E[1]);

                Assert(PlayingSound->SamplesPlayed >= 0);
                uint32 ChunksToMix = TotalChunksToMix;
                // uint32 FirstSampleIndex = ;
                real32 RealChunksRemingInSound =
                    (LoadedSound->SampleCount - RoundReal32ToInt32(PlayingSound->SamplesPlayed)) /
                    dSampleChunk;
                uint32 ChunksRemainingInSound = RoundReal32ToUInt32(RealChunksRemingInSound);
                if (ChunksToMix > ChunksRemainingInSound) {
                    ChunksToMix = ChunksRemainingInSound;
                }
                uint32 VolumeEndsAt[AudioStateOutputChannelCount] = {};
                for (uint32 ChannelIndex = 0; ChannelIndex < ArrayCount(VolumeEndsAt);
                     ++ChannelIndex) {
                    if (dVolumeChunk.E[ChannelIndex] != 0.0f) {
                        real32 DeltaVolume =
                            PlayingSound->TargetVolume.E[ChannelIndex] - Volume.E[ChannelIndex];
                        uint32 VolumeChunkCount =
                            (uint32)((DeltaVolume / dVolumeChunk.E[ChannelIndex]) + 0.5);
                        if (ChunksToMix > VolumeChunkCount) {
                            ChunksToMix = VolumeChunkCount;
                            VolumeEndsAt[ChannelIndex] = VolumeChunkCount;
                        }
                    }
                }
                real32 BeginSamplePosition = PlayingSound->SamplesPlayed;
                real32 EndSamplePosition = BeginSamplePosition + ChunksToMix * dSampleChunk;
                real32 LoopIndexC = (EndSamplePosition - BeginSamplePosition) / (real32)ChunksToMix;
                for (uint32 LoopIndex = 0; LoopIndex < ChunksToMix; ++LoopIndex) {
                    real32 SamplePosition = BeginSamplePosition + LoopIndexC * (real32)LoopIndex;

#if 1
                    __m128 SamplePos = _mm_setr_ps(SamplePosition + 0.0f * dSample,  //
                                                   SamplePosition + 1.0f * dSample,  //
                                                   SamplePosition + 2.0f * dSample,  //
                                                   SamplePosition + 3.0f * dSample);
                    __m128i SampleIndex = _mm_cvttps_epi32(SamplePos);
                    __m128 Frac = _mm_sub_ps(SamplePos, _mm_cvtepi32_ps(SampleIndex));

                    __m128 SampleValueF =
                        _mm_setr_ps(LoadedSound->Samples[0][((int *)&SampleIndex)[0]],  //
                                    LoadedSound->Samples[0][((int *)&SampleIndex)[1]],  //
                                    LoadedSound->Samples[0][((int *)&SampleIndex)[2]],  //
                                    LoadedSound->Samples[0][((int *)&SampleIndex)[3]]);

                    __m128 SampleValueC =
                        _mm_setr_ps(LoadedSound->Samples[0][((int *)&SampleIndex)[0] + 1],  //
                                    LoadedSound->Samples[0][((int *)&SampleIndex)[1] + 1],  //
                                    LoadedSound->Samples[0][((int *)&SampleIndex)[2] + 1],  //
                                    LoadedSound->Samples[0][((int *)&SampleIndex)[3] + 1]);
                    __m128 SampleValue =
                        _mm_add_ps(_mm_mul_ps(_mm_sub_ps(One, Frac), SampleValueF),  //
                                   _mm_mul_ps(Frac, SampleValueC));                  // Lerp
#else
                    __m128 SampleValue = _mm_setr_ps(
                        LoadedSound
                            ->Samples[0][RoundReal32ToInt32(SamplePosition + 0.0f * dSample)],
                        LoadedSound
                            ->Samples[0][RoundReal32ToInt32(SamplePosition + 1.0f * dSample)],
                        LoadedSound
                            ->Samples[0][RoundReal32ToInt32(SamplePosition + 2.0f * dSample)],
                        LoadedSound
                            ->Samples[0][RoundReal32ToInt32(SamplePosition + 3.0f * dSample)]);
#endif

                    __m128 D0 = _mm_load_ps((float *)&Dest0[0]);
                    __m128 D1 = _mm_load_ps((float *)&Dest1[0]);

                    D0 =
                        _mm_add_ps(D0, _mm_mul_ps(_mm_mul_ps(MasterVolume0, Volume0), SampleValue));
                    D1 =
                        _mm_add_ps(D1, _mm_mul_ps(_mm_mul_ps(MasterVolume1, Volume1), SampleValue));

                    _mm_store_ps((float *)&Dest0[0], D0);
                    _mm_store_ps((float *)&Dest1[0], D1);

                    ++Dest0;
                    ++Dest1;
                    Volume0 = _mm_add_ps(Volume0, dVolumeChunk0);
                    Volume1 = _mm_add_ps(Volume1, dVolumeChunk1);
                }
                PlayingSound->CurrentVolume.E[0] = ((real32 *)&Volume0)[0];
                PlayingSound->CurrentVolume.E[1] = ((real32 *)&Volume1)[1];
                for (uint32 ChannelIndex = 0; ChannelIndex < ArrayCount(VolumeEndsAt);
                     ++ChannelIndex) {
                    if (ChunksToMix == VolumeEndsAt[ChannelIndex]) {
                        PlayingSound->CurrentVolume.E[ChannelIndex] =
                            PlayingSound->TargetVolume.E[ChannelIndex];
                        PlayingSound->dCurrentVolume.E[ChannelIndex] = 0.0f;
                    }
                }
                PlayingSound->SamplesPlayed = EndSamplePosition;
                Assert(TotalChunksToMix >= ChunksToMix);
                TotalChunksToMix -= ChunksToMix;
                if (ChunksToMix == ChunksRemainingInSound) {
                    if (IsValid(NextSoundInChina)) {
                        PlayingSound->ID = NextSoundInChina;
                        Assert(PlayingSound->SamplesPlayed >= LoadedSound->SampleCount);
                        PlayingSound->SamplesPlayed -= (real32)LoadedSound->SampleCount;
                        if (PlayingSound->SamplesPlayed < 0) {
                            PlayingSound->SamplesPlayed = 0;
                        }
                    } else {
                        SoundFinished = true;
                    }
                }
            } else {
                LoadSound(Assets, PlayingSound->ID);
                break;
            }
        }
        if (SoundFinished) {
            *PlayingSoundPtr = PlayingSound->Next;
            PlayingSound->Next = AudioState->FirstFreePlayingSound;
            AudioState->FirstFreePlayingSound = PlayingSound;
        } else {
            PlayingSoundPtr = &PlayingSound->Next;
        }
    }
    // 转换到16-bit
    {
        __m128 *Source0 = RealChannel0;
        __m128 *Source1 = RealChannel1;
        __m128i *SampleOut = (__m128i *)SoundBuffer->Samples;
        // 循环写入样本到第一段区域
        for (uint32 SampleIndex = 0;    //
             SampleIndex < ChunkCount;  //
             ++SampleIndex) {
            __m128 S0 = _mm_load_ps((float *)Source0++);
            __m128 S1 = _mm_load_ps((float *)Source1++);

            __m128i L = _mm_cvtps_epi32(S0);
            __m128i R = _mm_cvtps_epi32(S1);

            __m128i LR0 = _mm_unpacklo_epi32(L, R);
            __m128i LR1 = _mm_unpackhi_epi32(L, R);

            __m128i S01 = _mm_packs_epi32(LR0, LR1);
            *SampleOut++ = S01;
        }
    }
    EndGeneration(Assets, GenerationID);
    EndTemporaryMemory(MixerMemory);
}

void InitializeAudioState(audio_state *AudioState, memory_arena *Arena) {
    AudioState->Arena = Arena;
    AudioState->FirstFreePlayingSound = 0;
    AudioState->FirstPlayingSound = 0;
    AudioState->MasterVolume = {1.0f, 1.0f};
}

GAME_GET_SOUND_SAMPLES(GameGetSoundSamples) {
    game_state *GameState = (game_state *)Memory->PermanentStorage;
    transient_state *TranState = (transient_state *)Memory->TransientStorage;
    OutputPlayingSound(&GameState->AudioState, SoundBuffer, TranState->Assets,
                       TranState->TransientArena);
    // 输出声音，根据 ToneHz 控制音调频率
    // GameOutputSound(GameState, SoundBuffer, 440);
}

internal playing_sound *PlaySound(audio_state *AudioState, sound_id SoundID) {
    if (!AudioState->FirstFreePlayingSound) {
        AudioState->FirstFreePlayingSound = PushStruct(AudioState->Arena, playing_sound);
        AudioState->FirstFreePlayingSound->Next = 0;
    }
    playing_sound *PlayingSound = AudioState->FirstFreePlayingSound;
    AudioState->FirstFreePlayingSound = PlayingSound->Next;
    PlayingSound->CurrentVolume = PlayingSound->TargetVolume = {1.0f, 1.0f};
    PlayingSound->dCurrentVolume = {0, 0};
    PlayingSound->ID = SoundID;
    PlayingSound->Next = AudioState->FirstPlayingSound;
    PlayingSound->SamplesPlayed = 0;
    PlayingSound->dSample = 1.0f;
    AudioState->FirstPlayingSound = PlayingSound;
    return PlayingSound;
}

void ChangeVolume(audio_state *AudioState, playing_sound *Sound, real32 FadeDurationInSeconds,
                  v2 Volume) {
    (void)AudioState;
    if (Sound) {
        if (FadeDurationInSeconds <= 0.0f) {
            Sound->CurrentVolume = Sound->TargetVolume = Volume;
        } else {
            real32 OneOverFade = 1.0f / FadeDurationInSeconds;
            Sound->dCurrentVolume = OneOverFade * (Sound->TargetVolume - Sound->CurrentVolume);
            Sound->TargetVolume = Volume;
        }
    }
}

internal void ChangePitch(game_state *AudioState, playing_sound *Sound, real32 dSample) {
    (void)AudioState;
    if (Sound) {
        Sound->dSample = dSample;
    }
}